/* Copyright 2008 Robert C. Brinson <rbrinson@gmail.com>
 * 
 * This file is part of PictureTagger.
 *
 * PictureTagger is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * PictureTagger is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PictureTagger.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.ComponentModel;
using Gtk;
using Gdk;
using PictureTagger.PictureCollection;
using PictureTagger.PictureTaggerUtility;

namespace PictureTagger.PictureTagger
{
	public partial class MainWindow: Gtk.Window
    {
        #region Variables
        private ListStore _photoCollection;
		private PictureRepository _repository;
		private const string _edited = "*";
		private string _prevHeadline = String.Empty;
		private string _prevCaption = String.Empty;
		private string _prevKeywords = String.Empty;
		private BackgroundWorker _loadPhotoCollection;
		private BackgroundWorker _savePhotoCollection;
		private Cursor _regularCursor;
		private Cursor _workingCursor;
        #endregion

        #region Constructors
        public MainWindow (): base (Gtk.WindowType.Toplevel)
		{
			Build ();
			_loadPhotoCollection = new BackgroundWorker();
			_savePhotoCollection = new BackgroundWorker();
			_loadPhotoCollection.DoWork += new DoWorkEventHandler(ProcessPhotoCollection_DoWork);
			_loadPhotoCollection.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ProcessPhotoCollection_RunWorkerCompleted);
			_savePhotoCollection.DoWork += new DoWorkEventHandler(SavePhotoCollection_DoWork);
			_savePhotoCollection.RunWorkerCompleted += new RunWorkerCompletedEventHandler(SavePhotoCollection_RunWorkerCompleted);
			_regularCursor = new Cursor(CursorType.LeftPtr);
			_workingCursor = new Cursor(CursorType.Watch);
			lblCurrentDirectory.Text = "No Folder Selected";
			lblStatus.Text = "Ready";
        }
        #endregion

        #region Events
        private void ProcessPhotoCollection_DoWork(object sender, DoWorkEventArgs e)
		{
			e.Result = new PictureRepository(ConfigurationManager.AppSettings.Get("ExifToolPath"), (string)e.Argument);
		}
		
		private void ProcessPhotoCollection_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if (e.Error != null)
			{
				Application.Invoke(delegate { DisplayInformationalMessages(MessageType.Error, ButtonsType.Ok, "An error occured while trying to load photographs from the selected folder. " + e.Error.Message); SetReadyToWorkDisplay(); });
			}
			else
			{
				Application.Invoke(delegate { BindPicturesToIconView((PictureRepository)e.Result); });
			}
		}
		
		private void SavePhotoCollection_DoWork(object sender, DoWorkEventArgs e)
		{
			e.Result = _repository.SavePictureCollection();
		}
		
		private void SavePhotoCollection_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if (e.Error != null)
			{
				Application.Invoke(delegate { DisplayInformationalMessages(MessageType.Error, ButtonsType.Ok, "An error occured while trying to save the metadata to your photographs. " + e.Error.Message); });
			}
			else
			{
				if ((bool) e.Result)
				{
					Application.Invoke(delegate { DisplayInformationalMessages(MessageType.Info, ButtonsType.Ok, "The metadata tags were successfully saved."); chkHeadline.Active = false; chkCaption.Active = false; chkKeywords.Active = false;});
				}
				else
				{
					Application.Invoke(delegate { DisplayInformationalMessages(MessageType.Error, ButtonsType.Ok, "An error occured while trying to save the metadata to your photographs."); });
				}
			}
		}
		
		protected void OnDeleteEvent (object sender, DeleteEventArgs a)
		{
			bool quit = true;
			
			if (_repository != null)
			{
				if (_repository.PictureCollectionIsDirty())
				{
					quit = ConfirmDirtyAction();
				}
			}
			
			if (quit)
			{
				Application.Quit ();
			}
			a.RetVal = true;
		}
		
		protected virtual void mnuOpen_Activated (object sender, System.EventArgs e)
		{
			bool continueOpen = true;
			if (_repository != null)
			{
				if (_repository.PictureCollectionIsDirty())
				{
					continueOpen = ConfirmDirtyAction();
				}
			}
			
			if (continueOpen)
			{
				String selectedFolder = String.Empty;
				FileChooserDialog fc = new FileChooserDialog("Select a folder containing photographs."
				                                             , this
				                                             , FileChooserAction.SelectFolder
				                                             , "Cancel",ResponseType.Cancel
				                                             , "Open",ResponseType.Accept);
				
				if (lblCurrentDirectory.Text == String.Empty || lblCurrentDirectory.Text == "No Folder Selected")
				{
					fc.SetCurrentFolder(Environment.GetFolderPath(Environment.SpecialFolder.Personal));
				}
				else
				{
					fc.SetCurrentFolder(lblCurrentDirectory.Text);
				}
				
				if (fc.Run() == (int)ResponseType.Accept)
				{
					selectedFolder = fc.Filename;
				}
				fc.Destroy();
				
				if(selectedFolder != String.Empty)
				{
					SetLoadingPicturesDisplay(selectedFolder);
					_loadPhotoCollection.RunWorkerAsync(selectedFolder);
				}
			}
		}
		
		protected virtual void mnuSave_Activated (object sender, System.EventArgs e)
		{
			Save();
		}
		
		protected virtual void mnuReset_Activated (object sender, System.EventArgs e)
		{
			Reset();
		}

		protected virtual void mnuQuit_Activated (object sender, System.EventArgs e)
		{
			bool quit = true;
			
			if (_repository != null)
			{
				if (_repository.PictureCollectionIsDirty())
				{
					quit = ConfirmDirtyAction();
				}
			}
			
			if (quit)
			{
				Application.Quit ();
			}
		}

		protected virtual void mnuAbout_Activated (object sender, System.EventArgs e)
		{
			const string license = 
@"PictureTagger is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

PictureTagger is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PictureTagger.  If not, see <http://www.gnu.org/licenses/>.";
			AboutDialog about = new AboutDialog();
			Assembly asm = Assembly.GetExecutingAssembly();
			about.ProgramName = (asm.GetCustomAttributes(typeof(AssemblyTitleAttribute), false)[0] as AssemblyTitleAttribute).Title;
			about.Version = asm.GetName().Version.ToString();
			about.Comments = (asm.GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false)[0] as AssemblyDescriptionAttribute).Description;
			about.Copyright = (asm.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false)[0] as AssemblyCopyrightAttribute).Copyright;
			about.License = license;
			about.Authors = new string[] {"Robert C. Brinson <rbrinson@gmail.com>"};

			about.Run();
			about.Destroy();
		}

		protected virtual void ivwPictures_SelectionChanged (object sender, System.EventArgs e)
		{			
			string path = String.Empty;
			TreePath[] selections = ivwPictures.SelectedItems;
			if (selections.Length > 0)
			{
				TreeIter iter;
				_photoCollection.GetIter(out iter, selections[0]);
				path = (string) _photoCollection.GetValue(iter, 1);
				
				if (path != String.Empty)
				{
					chkHeadline.Sensitive = true;
					chkCaption.Sensitive = true;
					chkKeywords.Sensitive = true;
					
					DisplayMetaData(path);
				}
			}
			else
			{
				chkHeadline.Active = false;
				chkCaption.Active = false;
				chkKeywords.Active = false;
				
				ClearMetaDataWidgets();
			}
		}
		
		protected virtual void chkHeadline_Clicked (object sender, System.EventArgs e)
		{
			txtHeadline.Sensitive = !txtHeadline.Sensitive;
			if(!txtHeadline.Sensitive)
			{
				ResetMetaDataTag(ExifToolTags.IPTC_HEADLINE);
			}
		}

		protected virtual void chkCaption_Clicked (object sender, System.EventArgs e)
		{
			txtvwCaption.Sensitive = !txtvwCaption.Sensitive;
			if(!txtvwCaption.Sensitive)
			{
				ResetMetaDataTag(ExifToolTags.IPTC_CAPTION);
			}
		}

		protected virtual void chkKeywords_Clicked (object sender, System.EventArgs e)
		{
			txtvwKeywords.Sensitive = !txtvwKeywords.Sensitive;
			if(!txtvwKeywords.Sensitive)
			{
				ResetMetaDataTag(ExifToolTags.IPTC_KEYWORDS);
			}
		}
		
		protected virtual void txtHeadline_FocusInEvent (object o, Gtk.FocusInEventArgs args)
		{
			_prevHeadline = txtHeadline.Text;
		}

		protected virtual void txtHeadline_FocusOutEvent (object o, Gtk.FocusOutEventArgs args)
		{
			if (_prevHeadline != txtHeadline.Text)
			{
				SetMetaDataTag(ExifToolTags.IPTC_HEADLINE, txtHeadline.Text);
				SetDirtyDirectoryIndicator();
			}
		}
		
		protected virtual void txtvwCaption_FocusInEvent (object o, Gtk.FocusInEventArgs args)
		{
			_prevCaption = txtvwCaption.Buffer.Text;
		}

		protected virtual void txtvwCaption_FocusOutEvent (object o, Gtk.FocusOutEventArgs args)
		{
			if (_prevCaption != txtvwCaption.Buffer.Text)
			{
				SetMetaDataTag(ExifToolTags.IPTC_CAPTION, txtvwCaption.Buffer.Text);
				SetDirtyDirectoryIndicator();
			}
		}

		protected virtual void txtvwKeywords_FocusInEvent (object o, Gtk.FocusInEventArgs args)
		{
			_prevKeywords = txtvwKeywords.Buffer.Text;
		}

		protected virtual void txtvwKeywords_FocusOutEvent (object o, Gtk.FocusOutEventArgs args)
		{
			if (_prevKeywords != txtvwKeywords.Buffer.Text)
			{
				SetMetaDataTag(ExifToolTags.IPTC_KEYWORDS, txtvwKeywords.Buffer.Text);
				SetDirtyDirectoryIndicator();
			}
		}
		
		protected virtual void btnReset_Clicked (object sender, System.EventArgs e)
		{
			Reset();
		}
		
		protected virtual void btnSave_Clicked (object sender, System.EventArgs e)
		{
			Save();
        }
        #endregion

        #region Methods
        private ResponseType DisplayInformationalMessages(MessageType msgType, ButtonsType btnType, string msg)
		{
			MessageDialog infoDialog = new MessageDialog(this, DialogFlags.DestroyWithParent, msgType, btnType, msg);
			ResponseType response = (ResponseType) infoDialog.Run();
			infoDialog.Destroy();
            return response;
		}
		
		private void SetLoadingPicturesDisplay(string path)
		{
			DisableSaveReset();
			lblStatus.Text = "Loading Pictures...";
			GdkWindow.Cursor = _workingCursor;
			lblCurrentDirectory.Text = path;
			ClearMetaDataWidgets();
			ivwPictures.Model = null;
		}
		
		private void SetSavingPicturesDisplay()
		{
			DisableSaveReset();
			lblStatus.Text = "Saving Pictures...";
			ivwPictures.Sensitive = false;
			GdkWindow.Cursor = _workingCursor;
		}
		
		private void SetReadyToWorkDisplay()
		{
			EnableSaveReset();
			lblStatus.Text = "Ready";
			ivwPictures.Sensitive = true;
			GdkWindow.Cursor = _regularCursor;
		}
		
		private void SetDirtyDirectoryIndicator()
		{
			if(!lblCurrentDirectory.Text.StartsWith(_edited))
			{
				lblCurrentDirectory.Text = _edited + lblCurrentDirectory.Text;
			}
		}
		
		private void ClearDirtyDirectoryIndicator()
		{
			if(lblCurrentDirectory.Text.StartsWith(_edited))
			{
				lblCurrentDirectory.Text = lblCurrentDirectory.Text.Substring(1);
			}
		}
		
		private void BindPicturesToIconView(PictureRepository photos)
		{
			Pixbuf pix;
			_repository = photos;
			_photoCollection = new ListStore(typeof(Pixbuf), typeof(String));
			
			if (_repository != null)
			{
				foreach (string file in _repository.PictureCollection.Keys)
				{
					pix = new Pixbuf(_repository.PictureCollection[file].ThumbNail);
					_photoCollection.AppendValues(new System.Object[] {pix, file});
				}
			}
			
			ivwPictures.Model = _photoCollection;
			ivwPictures.PixbufColumn = 0;
			ivwPictures.TextColumn = 1;
			ivwPictures.SelectionMode = SelectionMode.Multiple;
			ivwPictures.Columns = 0;
			
			SetReadyToWorkDisplay();
		}
		
		private void SetMetaDataTag(string metaDataName, string metaDataValue)
		{
			string path = String.Empty;
			TreePath[] selections = ivwPictures.SelectedItems;
			if (selections.Length > 0)
			{
				foreach (TreePath selection in selections)
				{
					TreeIter iter;
					_photoCollection.GetIter(out iter, selection);
					path = (string) _photoCollection.GetValue(iter, 1);
					
					_repository.SetMetaDataTag(path, metaDataName, metaDataValue);
				}
			}
		}
		
		private void ResetMetaDataTag(string metaDataName)
		{
			string path = String.Empty;
			TreePath[] selections = ivwPictures.SelectedItems;
			if (selections.Length > 0)
			{
				foreach (TreePath selection in selections)
				{
					TreeIter iter;
					_photoCollection.GetIter(out iter, selection);
					path = (string) _photoCollection.GetValue(iter, 1);
					
					_repository.ResetMetaDataTag(path, metaDataName);
				}
				DisplayMetaData(path);
			}
		}
		
		private void Save()
		{
			if(_repository != null && _repository.PictureCollectionIsDirty())
			{
				SetSavingPicturesDisplay();
				_savePhotoCollection.RunWorkerAsync();
				SetReadyToWorkDisplay();
				ClearDirtyDirectoryIndicator();
			}
		}
		
		private void Reset()
		{
			if(_repository != null && _repository.PictureCollectionIsDirty())
			{
	            ResponseType response = DisplayInformationalMessages(MessageType.Warning, ButtonsType.YesNo, "Reseting PictureTagger will cause all changes since the last save to be lost. Are you sure you want to continue?");
				
				if (response == ResponseType.Yes)
				{
					DisableSaveReset();
					lblStatus.Text = "Reseting Pictures...";
					_repository.ResetPictureCollection();
					lblStatus.Text = "Ready";
					EnableSaveReset();
				}
				
				TreePath[] selections = ivwPictures.SelectedItems;
				if (selections.Length > 0)
				{
					string path = String.Empty;
					TreeIter iter;
					_photoCollection.GetIter(out iter, selections[0]);
					path = (string) _photoCollection.GetValue(iter, 1);
					DisplayMetaData(path);
				}
				
				ClearDirtyDirectoryIndicator();
			}
		}
		
		private void ClearMetaDataWidgets()
		{
			txtHeadline.Text = String.Empty;
			txtvwCaption.Buffer.Text = String.Empty;
			txtvwKeywords.Buffer.Text = String.Empty;
		}
		
		private void DisplayMetaData(string key)
		{
			StringBuilder keywords = new StringBuilder();
			ClearMetaDataWidgets();
			
			chkHeadline.Active = _repository.PictureCollection[key].IsHeadlineDirty;
			txtHeadline.Text = _repository.PictureCollection[key].Headline;
			
			chkCaption.Active = _repository.PictureCollection[key].IsCaptionDirty;
			txtvwCaption.Buffer.Text = _repository.PictureCollection[key].Caption;
			
			chkKeywords.Active = _repository.PictureCollection[key].IsKeywordsDirty;
			foreach (string keyword in _repository.PictureCollection[key].Keywords)
			{
				keywords.Append(keyword);
				keywords.Append(", ");
			}
			if (keywords.Length > 0)
			{
				keywords = keywords.Remove(keywords.Length - 2, 2);
			}
			txtvwKeywords.Buffer.Text = keywords.ToString();
		}
		
		private void EnableSaveReset()
		{
			if (!btnReset.Sensitive)
			{
				mnuSave.Sensitive = true;
				mnuReset.Sensitive = true;
				btnReset.Sensitive = true;
				btnSave.Sensitive = true;
			}
		}
		
		private void DisableSaveReset()
		{
			if (btnReset.Sensitive)
			{
				mnuSave.Sensitive = false;
				mnuReset.Sensitive = false;
				btnReset.Sensitive = false;
				btnSave.Sensitive = false;
			}
		}
		
		private bool ConfirmDirtyAction()
		{
			bool continueDirtyAction = true;
            ResponseType response = DisplayInformationalMessages(MessageType.Warning, ButtonsType.YesNo, "There are pictures in PictureTagger that have been modified but not saved. Are you sure you want to continue?");

			if (response == ResponseType.No)
			{
				continueDirtyAction = false;
			}
			
			return continueDirtyAction;
        }
        #endregion
    }
}